package com.sl.au.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.sl.au.entity.DateQueryItem;
import com.sl.au.entity.DoubleQueryItem;
import com.sl.au.entity.IntegerQueryItem;
import com.sl.au.entity.NumQueryItem;
import com.sl.au.entity.User;
import com.sl.au.service.QueryService;
import com.sl.utils.DataConverter;

@Service("queryService")
@Transactional
public class QueryServiceImpl extends BaseServiceImpl implements QueryService {

	@PersistenceContext
	private EntityManager em;

	@Override

	public void buildDateCriterionHql(StringBuilder hql, Map<String, Object> paramsMap, String propertyName,
			Date startDate, Date endDate) {
		String start = propertyName + "StartDate";
		String end = propertyName + "EndDate";
		if (startDate != null && endDate != null) {
			hql.append(" and ( e.").append(propertyName).append(" between ").append(":" + start + " and ")
					.append(":" + end + ")");
			paramsMap.put(start, startDate);
			paramsMap.put(end, endDate);

		} else if (startDate != null) {
			hql.append(" and  (e.").append(propertyName).append(" >= :" + start + ")");
			paramsMap.put(start, startDate);
		} else if (endDate != null) {
			hql.append(" and (e.").append(propertyName).append(" <=:" + end + ")");
			paramsMap.put(end, endDate);
		}

	}

	@Override
	public void buildStringCriterionHql(StringBuilder hql, Map<String, Object> paramsMap, String propertyName,
			String value) {
		String namedParamName = propertyName;
		if (propertyName.contains(".")) {
			namedParamName = DataConverter.getLastSplitPart(propertyName, ".");
		}

		if (StringUtils.hasText(value)) {
			if (value.equals("NULL")) {
				hql.append(" and (e.").append(propertyName).append(" is NULL)");
			} else {
				hql.append(" and (e.").append(propertyName).append(" like  :" + namedParamName + " )");
				paramsMap.put(namedParamName, value);
			}
		}
	}

	@Override
	public void buildFloatCriterionHql(StringBuilder hql, Map<String, Object> paramsMap, String propertyName, float startNum, float endNum) {
		
		String start = propertyName + "StartNum";
		String end = propertyName + "EndNum";
		if (startNum != 0 && endNum != 0) {
			hql.append(" and ( e.").append(propertyName).append(" >= :" + start + ") and ( e.").append(propertyName).append(" <= :" + end + ") ");
			paramsMap.put(start, startNum);
			paramsMap.put(end, endNum);

		} else if (startNum != 0) {
			hql.append(" and  (e.").append(propertyName).append(" >= :" + start + ")");
			paramsMap.put(start, startNum);
		} else if (endNum != 0) {
			hql.append(" and (e.").append(propertyName).append(" <=:" + end + ")");
			paramsMap.put(end, endNum);
		}

	}
	@Override
	public void buildIntCriterionHql(StringBuilder hql, Map<String, Object> paramsMap, String propertyName, int startNum, int endNum) {
		
		String start = propertyName + "StartNum";
		String end = propertyName + "EndNum";
		if (startNum != 0 && endNum != 0) {
			hql.append(" and ( e.").append(propertyName).append(" >= :" + start + ") and ( e.").append(propertyName).append(" <= :" + end + ") ");
			paramsMap.put(start, startNum);
			paramsMap.put(end, endNum);
			
		} else if (startNum != 0) {
			hql.append(" and  (e.").append(propertyName).append(" >= :" + start + ")");
			paramsMap.put(start, startNum);
		} else if (endNum != 0) {
			hql.append(" and (e.").append(propertyName).append(" <=:" + end + ")");
			paramsMap.put(end, endNum);
		}
		
	}
	
	@Override
	public void buildDoubleCriterionHql(StringBuilder hql, Map<String, Object> paramsMap, String propertyName, double startNum, double endNum) {
		
		String start = propertyName + "StartNum";
		String end = propertyName + "EndNum";
		if (startNum != 0 && endNum != 0) {
			hql.append(" and ( e.").append(propertyName).append(" >= :" + start + ") and ( e.").append(propertyName).append(" <= :" + end + ") ");
			paramsMap.put(start, startNum);
			paramsMap.put(end, endNum);
			
		} else if (startNum != 0) {
			hql.append(" and  (e.").append(propertyName).append(" >= :" + start + ")");
			paramsMap.put(start, startNum);
		} else if (endNum != 0) {
			hql.append(" and (e.").append(propertyName).append(" <=:" + end + ")");
			paramsMap.put(end, endNum);
		}
		
	}

	@Override
	public <T> List<T> combinedQueryHql(String entityName, String selectedProperty, Map<String, Object> map, int start,
			int length, User user, String orderName, String dir) {
		Map<String, Object> namedParamsMap = new HashMap<>();
		StringBuilder hql = this.buildCombinedQueryHql(entityName, selectedProperty, map, namedParamsMap, null, user);

		hql.append(" order by " + orderName + " " + dir);

		Query query = em.createQuery(hql.toString());

		query.setFirstResult(start).setMaxResults(length);

		return this.executeHql(query, namedParamsMap);

	}

	@Override
	public long combinedQueryCountHql(String entityName, Map<String, Object> map) {
		@SuppressWarnings("unchecked")
		HashMap<String, Object> qMap = (HashMap<String, Object>) map.get("qMap");
		HashMap<String, Object> namedParamsMap = new HashMap<>();
		StringBuilder hql = new StringBuilder("select count (*) from " + entityName + "  e where (e.id is not null) ");
		for (String key : qMap.keySet()) {
			Object value = qMap.get(key);
			if (value instanceof DateQueryItem) {
				DateQueryItem d = (DateQueryItem) value;
				this.buildDateCriterionHql(hql, namedParamsMap, d.getPropertyName(), d.getStartDate(), d.getEndDate());
			}
			if (value instanceof String) {
				this.buildStringCriterionHql(hql, namedParamsMap, key, (String) value);
			}
		}
		Query query = em.createQuery(hql.toString());

		this.setNamedParams(query, namedParamsMap);
		return (long) query.getSingleResult();
	}

	@Override
	public StringBuilder buildCombinedQueryHql(String entityName, String selectedProperty, Map<String, Object> map,
			Map<String, Object> namedParamsMap, Map<String, Object> numMap, User user) {
		@SuppressWarnings("unchecked")
		HashMap<String, Object> qMap = (HashMap<String, Object>) map.get("qMap");
		StringBuilder hql = new StringBuilder();
		if (StringUtils.hasText(selectedProperty)) {
			if(selectedProperty.substring(0, 6).equals("sum(e.")){
				hql.append("select " + selectedProperty);
			}else{
				hql.append("select e." + selectedProperty);
			}
		} else {
			hql.append("select e");
		}
		hql.append(" from " + entityName);
		hql.append("  e where (e.id is not null) ");
		for (String key : qMap.keySet()) {
			Object value = qMap.get(key);
			if (value instanceof DateQueryItem) {
				DateQueryItem d = (DateQueryItem) value;
				this.buildDateCriterionHql(hql, namedParamsMap, d.getPropertyName(), d.getStartDate(), d.getEndDate());
			}
			if (value instanceof String) {
				this.buildStringCriterionHql(hql, namedParamsMap, key, (String) value);

			}
			if (value instanceof NumQueryItem) {
				NumQueryItem n = (NumQueryItem) value;
				this.buildFloatCriterionHql(hql, namedParamsMap, n.getPropertyName(), n.getStartNum(), n.getEndNum());
			}
			if (value instanceof IntegerQueryItem) {
				IntegerQueryItem n = (IntegerQueryItem) value;
				this.buildIntCriterionHql(hql, namedParamsMap, n.getPropertyName(), n.getStartNum(), n.getEndNum());
			}
			if (value instanceof DoubleQueryItem) {
				DoubleQueryItem n = (DoubleQueryItem) value;
				this.buildDoubleCriterionHql(hql, namedParamsMap, n.getPropertyName(), n.getStartNum(), n.getEndNum());
			}
			if (null == value && key.contains("isSucceed")) {
				// null 表示处理中
				if (entityName.equals("Chance"))
					hql.append(" and(e.isSucceed is null");
				else
					hql.append(" and(e.saleChance.isSucceed is null)");
			}
		}
//		for (String num : numMap.keySet()) {
//			Object value = numMap.get(num);
//			if (value instanceof NumQueryItem) {
//				NumQueryItem n = (NumQueryItem) value;
//				System.out.println(n.getPropertyName()+n.getStartNum()+ n.getEndNum());
//				this.buildIntCriterionHql(hql, namedParamsMap, n.getPropertyName(), n.getStartNum(), n.getEndNum());
//			}
//		}
		return hql;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> executeHql(Query query, Map<String, Object> namedParamsMap) {
		if (null != query && null != namedParamsMap) {
			this.setNamedParams(query, namedParamsMap);
			return query.getResultList();
		}
		return null;
	}

	@Override
	public void setNamedParams(Query query, Map<String, Object> namedParamsMap) {
		if (null == query || null == namedParamsMap)
			return;
		for (String key : namedParamsMap.keySet()) {
			if (namedParamsMap.get(key) instanceof Date)
				query.setParameter(key, namedParamsMap.get(key));
			if (namedParamsMap.get(key) instanceof String)
				query.setParameter(key, "%" + namedParamsMap.get(key).toString() + "%");
			if(namedParamsMap.get(key) instanceof Float)
				query.setParameter(key, namedParamsMap.get(key));
			if(namedParamsMap.get(key) instanceof Double)
				query.setParameter(key, namedParamsMap.get(key));
			if(namedParamsMap.get(key) instanceof Integer)
				query.setParameter(key, namedParamsMap.get(key));
		}
	}

	@Override
	public Query createQueryHql(String hql) {
		if (StringUtils.hasText(hql))
			return em.createQuery(hql);
		return null;
	}

	@Override
	public EntityManager getEM() {
		return this.em;
	}

}
